package com.akxy.pt.tsdb.influxdb;

import com.akxy.pt.tsdb.influxdb.operator.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Mengfly
 */
public class InfluxDb {

    private final List<Operator> operatorList = new ArrayList<>();

    public InfluxDb bucket(String bucket) {
        return addOperator(new BucketOperator(bucket));
    }

    public InfluxDb range(Date start, Date end) {
        return addOperator(new RangeOperator(start, end));
    }

    public InfluxDb filer(FilterOperator filterOperator) {
        return addOperator(filterOperator);
    }

    public InfluxDb aggregateWindow(String every, String fn) {
        return addOperator(new AggregateWindowOperator(every, fn));
    }

    public InfluxDb group(String... columns) {
        return addOperator(new GroupOperator(columns));
    }

    public InfluxDb limit(int limit, int offset) {
        return addOperator(new LimitOperator(limit, offset));
    }

    public InfluxDb measurement(String measurement) {
        return addOperator(new FilterOperator("_measurement", measurement));
    }

    private InfluxDb addOperator(Operator operator) {
        this.operatorList.add(operator);
        return this;
    }

    private InfluxDb script(String script) {
        return addOperator(new FluxScriptOperator(script));
    }

    public InfluxDb fieldAsCols() {
        return addOperator(new FieldAsColsOperator());
    }

    public InfluxDb window(String every) {
        return addOperator(new WindowOperator(every));
    }

    public <M> List<M> list(Class<M> measurementClass) {
        return InfluxDbUtil.query(this, measurementClass);
    }

    public long count(String column) {
        InfluxDb influxDb = newQuery(this).addOperator(new CountOperator(column));
        List<Long> count = InfluxDbUtil.query(influxDb, fluxRecord -> {
            Object valueByKey = fluxRecord.getValueByKey(column);
            if (valueByKey instanceof Long) {
                return (Long) valueByKey;
            }
            return null;
        });
        return count.stream().filter(Objects::nonNull).findFirst().orElse(0L);
    }

    public static InfluxDb newQuery(InfluxDb influxDb) {
        InfluxDb newQuery = new InfluxDb();
        for (Operator operator : influxDb.operatorList) {
            newQuery.addOperator(operator);
        }
        return newQuery;
    }

    public String queryString() {
        StringBuilder queryBuilder = new StringBuilder();
        List<ImportOperator> importOperators = operatorList.stream()
                .filter(operator -> operator instanceof ImportOperator)
                .map(operator -> (ImportOperator) operator)
                .collect(Collectors.toList());
        // fill import
        Set<String> imports = new HashSet<>();
        for (ImportOperator importOperator : importOperators) {
            imports.addAll(Arrays.asList(importOperator.importList()));
        }
        for (String imp : imports) {
            queryBuilder.append("import \"").append(imp).append("\"").append("\n");
        }
        if (!imports.isEmpty()) {
            queryBuilder.append("\n");
        }

        operatorList.sort(Comparator.comparing(Operator::order));
        for (Operator operator : operatorList) {
            queryBuilder.append(operator.operatorString());
        }
        return queryBuilder.toString();
    }
}
